Skip to content

[TrimmableTypeMap] Root manifest-referenced types as unconditional#11016

Closed
simonrozsival wants to merge 2 commits intodev/simonrozsival/trimmable-typemap-build-pipelinefrom
dev/simonrozsival/trimmable-typemap-post-manifest-rooting
Closed

[TrimmableTypeMap] Root manifest-referenced types as unconditional#11016
simonrozsival wants to merge 2 commits intodev/simonrozsival/trimmable-typemap-build-pipelinefrom
dev/simonrozsival/trimmable-typemap-post-manifest-rooting

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

Part of #10807
Depends on #10980

Summary

Ensures types declared only in hand-edited AndroidManifest.xml (no C# component attribute) are marked as unconditional and survive trimming.

Problem

A user can hand-edit their manifest with <activity android:name="com.example.MyLegacyActivity" /> where the type has [Register] but no [Activity] attribute. Without this fix, the scanner treats it as a conditional peer (3-arg TypeMapAttribute) and the linker may trim it → runtime crash.

Changes

  • JavaPeerInfo.cs: Changed IsUnconditional from init to set so it can be mutated after scanning
  • GenerateTrimmableTypeMap.cs: Added RootManifestReferencedTypes() that parses ManifestTemplate XML, extracts android:name values, and marks matching peers as unconditional
  • Tests: Verifies rooting works for known types and warns for missing types

// Multiple managed types can map to the same Java name (aliases).
var peersByDotName = new Dictionary<string, List<JavaPeerInfo>> (StringComparer.Ordinal);
foreach (var peer in allPeers) {
var dotName = peer.JavaName.Replace ('/', '.');
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is not enough - nested types would have $ that also needs to be replaced with . to form valid Java name. Is this the same problem we had elsewhere? Should this be a helper method? Or is there an existing helper method we could use?

Comment on lines +281 to +289
var manifestPath = Path.Combine (manifestDir, "AndroidManifest.xml");
File.WriteAllText (manifestPath, """
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.test">
<application>
<activity android:name="android.app.Activity" />
</application>
</manifest>
""");
Copy link
Copy Markdown
Member Author

@simonrozsival simonrozsival Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do this without writing to a file? Can we somehow change this to use memory stream instead? I believe we could just change GenerateTrimmableTypeMap so it expects the ManifestTemplate to be the memory stream (+ of course use memory stream pooling!)

@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch from 6f42b4d to 384a5a8 Compare March 25, 2026 16:16
simonrozsival and others added 2 commits March 25, 2026 17:17
When users hand-edit AndroidManifest.xml with component entries that have
no corresponding C# attribute (e.g. [Activity]), the referenced types
may be trimmed by the linker, causing runtime crashes when Android tries
to launch them.

This change adds RootManifestReferencedTypes() which parses the manifest
template before typemap assembly generation, finds matching peers by
their Java name, and marks them as unconditional so the linker preserves
them. A warning is logged for manifest-referenced types not found in any
scanned assembly (likely framework types).

Changes:
- JavaPeerInfo.IsUnconditional: changed from init to set
- GenerateTrimmableTypeMap: added RootManifestReferencedTypes method
- Added two tests for rooting and warning behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Parse the user's AndroidManifest.xml template to find hand-edited
component entries (activity, service, receiver, provider) and mark
matching Java peers as unconditional so they survive trimming.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-post-manifest-rooting branch from 1a0330a to 6831cef Compare March 25, 2026 16:27
@simonrozsival
Copy link
Copy Markdown
Member Author

Closing — will reimplement as a fresh PR stacked on the new PR split (#11032, #11033, #11034, #11035, #11036).

simonrozsival added a commit that referenced this pull request Mar 27, 2026
Parse the user's AndroidManifest.xml template for activity, service, receiver,
and provider elements with android:name attributes. Mark matching scanned
Java peer types as IsUnconditional = true so the ILLink TypeMap step preserves
them even if no managed code references them directly.

Changes:
- JavaPeerInfo.IsUnconditional: init → set (must be mutated after scanning)
- TrimmableTypeMapGenerator: add warn callback, RootManifestReferencedTypes()
  called between scanning and typemap generation
- GenerateTrimmableTypeMap task: pass Log.LogWarning as warn callback
- 4 new xUnit tests covering rooting, unresolved warnings, already-unconditional
  skip, and empty manifest

Replaces #11016 (closed — depended on old PR shape with TaskLoggingHelper).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant